<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml">

<head>
  <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
  <title>Choropleth Tutorial - Leaflet</title>
  <script include="jquery,moment" src="./static/libs/include-lib-local.js"></script>
  <script include="datastore,mapv,geohash,geojson,elasticsearch" src="./static/libs/include-leaflet-local.js"></script>
  <style>
    #mapid {
      position: absolute;
      width: 100%;
      height: 100%;
    }

    .info {
      padding: 6px 8px;
      font: 14px/16px Arial, Helvetica, sans-serif;
      background: white;
      background: rgba(255, 255, 255, 0.8);
      box-shadow: 0 0 15px rgba(0, 0, 0, 0.2);
      border-radius: 5px;
    }

    .info h4 {
      margin: 0 0 5px;
      color: #777;
    }

    .legend {
      line-height: 18px;
      color: #555;
    }

    .legend i {
      width: 18px;
      height: 18px;
      float: left;
      margin-right: 8px;
      opacity: 0.7;
    }
  </style>
</head>

<body>
  <div id="mapid"></div>
  <script>
    var mapExtent, mapLevel, startTime, endTime, timeSpeed, flashSpeed, currentTime, geojson;
    startTime = currentTime = Date.parse(new Date("2012-11-01 08:00:00")); //1516723200000; 毫秒
    endTime = Date.parse(new Date("2012-11-01 23:59:59")); // 1516809599000; 毫秒
    var timeSpeed = 60000; //10分钟的速度
    var flashSpeed = 1000; //1秒一刷新
    var map = L.map('mapid').setView([39.9, 116.35], 9);
    var timer = self.setInterval("clock()", flashSpeed);
    var client = new elasticsearch.Client({
      host: 'http://192.168.96.101:9200'
    });
    var mapvLayer, layerDataSet, layerOptions = getLayerOptions();

    L.tileLayer('https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=' +
      'sk.eyJ1IjoiY2hlbmdkYWRhIiwiYSI6ImNqZDFjaGo0ZjFzcnoyeG54enoxdnNuZHUifQ.hTWXXBUQ0wdGeuDF3GWeUw', {
        attribution: '<a href="#">MapBox</a>',
        maxZoom: 18,
        id: 'mapbox.streets',
      }).addTo(map);

    var info = L.control();
    info.onAdd = function (map) {
      this._div = L.DomUtil.create('div', 'info');
      this.update();
      return this._div;
    };
    info.update = function (time) {
      var timeformat = moment(time).format("YYYY-MM-DD HH:mm:ss");
      this._div.innerHTML = '<h4>北京时间</h4>' +
        '<b>' + timeformat + '</b><br>播放速度:' + timeSpeed / 1000 / 60 + '分钟</br>' +
        '<b>刷新速度:' + flashSpeed / 1000 + '秒</b>';
    };
    info.addTo(map);

    //--------------------------------------------------------------------
    //-----------------下面是实际核心代码，以前都是各种样式------------------
    //--------------------------------------------------------------------
    function clock() {
      mapExtent = map.getBounds();
      mapLevel = map.getZoom();
      currentTime = currentTime + timeSpeed;
      mapLevel = (mapLevel % 20) * 12 / 20 - 1;
      info.update(currentTime);
      loadData(mapLevel, mapExtent, currentTime, currentTime + timeSpeed);
    };

    function loadData(level, extent, startTimeStamp, endTimeStamp) {
      var bound = wrapLatLngBounds(extent);
      var prec = recalcLevel(level);
      client.search({
        index: 'sp_taxibj_200_2', //数据库名称
        //type: '2018-01-24',//数据库-表名称
        //也可以是2018-01-24,这里的type实际上上是指的每一张表,如果不设置的话就全库查询,
        //设置的话就针对这一天的表进行查询
        //scroll: '2m', // 30s or 2m 保持结果滚动2分钟或30秒
        body: {
          size: 0, //此处返回的是hits的数组大小
          query: {
            bool: {
              filter: {
                range: {
                  'GPS_DateTime': {
                    from: startTimeStamp,
                    to: endTimeStamp
                  }
                }
              }
            }
          },
          aggregations: {
            'mapExtent': {
              filter: {
                geo_bounding_box: {
                  'geometry': {
                    top_left: {
                      lat: bound.getNorthWest().lat,
                      lon: bound.getNorthWest().lng
                    },
                    bottom_right: {
                      lat: bound.getSouthEast().lat,
                      lon: bound.getSouthEast().lng
                    }
                  }
                }
              },
              aggregations: {
                'geohash': {
                  geohash_grid: {
                    field: "geometry",
                    precision: prec
                  }
                }
              }
            }
          }
        }
      }, getData);
    };

    var heatPoints = [];

    function getData(error, response) {
      layerDataSet = new mapv.DataSet(convertData(response));
      updateView();
    }

    function convertData(response) {
      var data = [];
      response.aggregations.mapExtent.geohash.buckets.forEach(function (bucket) {
        var coordinates = decodeGeoHash(bucket.key);
        data.push({
          geometry: {
            type: 'Point',
            coordinates: [coordinates.longitude[2], coordinates.latitude[2]]
          },
          count: bucket.doc_count
        });
      });
      return data;
    };

    function updateView() {
      if (!mapvLayer) {
        mapvLayer = new L.zondy.MapvLayer(map, layerDataSet, layerOptions, {
          noWrap: true
        }).addTo(map);
      } else {
        mapvLayer.updateData(layerDataSet, layerOptions);
      }
    }

    function getLayerOptions() {
      return {
        size: 20,
        gradient: {
          0: "rgba(49, 54, 149, 0)",
          0.2: "rgba(69,117,180, 0.7)",
          0.3: "rgba(116,173,209, 0.7)",
          0.4: "rgba(171,217,233, 0.7)",
          0.5: "rgba(224,243,248, 0.7)",
          0.6: "rgba(254,224,144,0.7)",
          0.7: "rgba(253,174,97,0.7)",
          0.8: "rgba(244,109,67,0.8)",
          0.9: "rgba(215,48,39,0.8)",
          0.95: "rgba(165, 0, 38,0.8)"
        },
        draw: 'heatmap'
      };
    }

    function recalcLevel(level) {
      var prec = 5;
      if (level <= 2) {
        prec = 5;
      } else if (level > 2 && level <= 5) {
        prec = 8;
      } else if (level > 5) {
        prec = 10;
      } else if (level > 8 && level <= 11) {
        prec = 11;
      } else if (level > 11 && level <= 13) {
        prec = 12;
      } else if (level > 13 && level <= 15) {
        prec = 12;
      } else if (level > 15) {
        prec = 12;
      };
      return prec;
    }

    function wrapLatLngBounds(extent) {
      var left = extent.getNorthWest().lng < -180 ? -180 : extent.getNorthWest().lng;
      var bottom = extent.getSouthEast().lat < -90 ? -90 : extent.getSouthEast().lat;
      var right = extent.getSouthEast().lng > 180 ? 180 : extent.getSouthEast().lng;
      var top = extent.getNorthWest().lat > 90 ? 90 : extent.getNorthWest().lat;
      return L.latLngBounds(L.latLng(bottom, left), L.latLng(top, right));
    }
  </script>
</body>

</html>